﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Windows;
using CloudShot.Editor.CanvasFigures;
using CloudShot.Properties;
using CloudShot.Utils.Tweens;
using Newtonsoft.Json;
using TheArtOfDev.HtmlRenderer.Core;
using TheArtOfDev.HtmlRenderer.Core.Entities;
using TheArtOfDev.HtmlRenderer.WinForms;

namespace CloudShot.Controls
{
  public class HtmlHint : IBoundingBoxFigure
  {
    private readonly Animator _animator;
    private readonly Dictionary<string, Bitmap> _cachedImages = new Dictionary<string, Bitmap>();

    private CssData _data;

    private class HtmlTooltipSettings
    {
      public string Css { get; set; }
      public string Text { get; set; }
    }

    private HtmlTooltipSettings _settings;

    private readonly HtmlContainer _container = new HtmlContainer();
    private string _text;


    private Point _location;


    private bool _visible;
    private bool _cropOverlaps;
    private bool _isAnimating = false;
    private float Opacity { get; set; }
    private bool ShouldShowHint
    {
      get { return _visible && !_cropOverlaps; }
    }


    public bool CropOverlaps
    {
      get { return _cropOverlaps; }
      set
      {
        if (_cropOverlaps == value)
          return;

        _cropOverlaps = value;
        AnimateOpacity(ShouldShowHint);
      }
    }

    public bool Visible
    {
      get { return _visible; }
      set
      {
        if (_visible == value)
          return;

        _visible = value;
        AnimateOpacity(ShouldShowHint);
      }
    }

    public bool IsHintVisibleForBoundingBox
    {
      get { return ShouldShowHint || _isAnimating; }
    }

  

    public string Text
    {
      get { return _text; }
      set
      {
        _text = value;

        if (_settings == null)
        {
          _settings = ReadSettigs();
          _data = HtmlRender.ParseStyleSheet(_settings.Css);
        }

        if (!string.IsNullOrWhiteSpace(_settings.Text))
          _text = _settings.Text;
      }
    }

    public HtmlHint(Animator animator)
    {
      _animator = animator;
      //Visible = true;
      Opacity = 0;
      _data = HtmlRender.ParseStyleSheet(HintResources.css);
      _container.RenderError += OnRenderError;
      _container.ImageLoad += OnImageLoad;
      _container.UseGdiPlusTextRendering = true;
    }


    public Rectangle GetBoundingBox()
    {
      if (string.IsNullOrEmpty(Text))
        return Rectangle.Empty;

      return Rectangle.Round(new RectangleF(_location, GetHintBitmap().Size));
    }


    private HtmlTooltipSettings ReadSettigs()
    {
      //const string fileName = "htmlTooltip.config";
      //if (File.Exists(fileName))
      //{
      //  string content = File.ReadAllText(fileName);

      //  return JsonConvert.DeserializeObject<HtmlTooltipSettings>(content);
      //}

      var set = new HtmlTooltipSettings()
      {
        Css = HintResources.css
      };

      //var defContent = JsonConvert.SerializeObject(set, Formatting.Indented);
      //File.WriteAllText(fileName, defContent);
      return set;
    }



    private void OnRenderError(object sender, HtmlRenderErrorEventArgs e)
    {
      Debug.WriteLine(e.Message);
    }

    private void OnImageLoad(object sender, HtmlImageLoadEventArgs htmlImageLoadEventArgs)
    {
      object imgObject = HintResources.ResourceManager.GetObject(htmlImageLoadEventArgs.Src);
      if (imgObject == null)
        return;

      var img = ((System.Drawing.Bitmap)(imgObject));
      htmlImageLoadEventArgs.Callback(img);
      htmlImageLoadEventArgs.Handled = true;
    }

    private Bitmap GetHintBitmap()
    {
      Bitmap hintBitmap;
      if (_cachedImages.TryGetValue(Text, out hintBitmap))
        return hintBitmap;

      _container.SetHtml(Text, _data);

      //required to measure image
      using (var bmp = new Bitmap(1, 1))
      {
        using (Graphics gBmp = Graphics.FromImage(bmp))
        {
          SetupGraphicsProps(gBmp);
          _container.PerformLayout(gBmp);
        }
      }

      hintBitmap = new Bitmap((int)_container.ActualSize.Width, (int)_container.ActualSize.Height);
      using (Graphics gBmp = Graphics.FromImage(hintBitmap))
      {
        SetupGraphicsProps(gBmp);
        _container.PerformPaint(gBmp);
      }

      _cachedImages[Text] = hintBitmap;

      return hintBitmap;
    }

    public void PerformLayout(Rectangle bounds)
    {
      if (!Visible || string.IsNullOrEmpty(Text))
        return;

      Bitmap bitmap = GetHintBitmap();
      const int bottomOffset = 100;
      int x = bounds.X + (bounds.Width - bitmap.Width) / 2;
      int y = (bounds.Bottom - Math.Max(bitmap.Height, bottomOffset));
      _location = new Point(x, y);
    }


    private void AnimateOpacity(bool show)
    {
      _isAnimating = true;
      int duration = show ? 150 : 600;

      //TODO refactor animation call
      var adapter = new FloatAnimationAdapter<HtmlHint>(this, hint => hint.Opacity, show ? 1 : 0);
     _animator.StartAnimation(adapter, duration, () => { _isAnimating = false; }, EasingType.Quintic);
    }


    public void Draw(Graphics graphics)
    {
      if (Opacity <= 0 || string.IsNullOrEmpty(Text))
        return;

      Bitmap bmp = GetHintBitmap();

      var matrix = new ColorMatrix
      {
        Matrix33 = Opacity 
      };

      ImageAttributes attributes = new ImageAttributes();
      attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);


      var state = graphics.Save();
      graphics.SmoothingMode = SmoothingMode.None;
      graphics.InterpolationMode = InterpolationMode.High;
      graphics.DrawImage(bmp, new Rectangle(_location.X, _location.Y, bmp.Width, bmp.Height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, attributes);
      //graphics.DrawImage(bitmap, (int)_location.X, (int)_location.Y);
      graphics.Restore(state);
    }

    private static void SetupGraphicsProps(Graphics gBmp)
    {
      //TODO same setting in Canvas control. Try to reuse?
      gBmp.SmoothingMode = SmoothingMode.HighQuality;
      gBmp.InterpolationMode = InterpolationMode.HighQualityBilinear;
      gBmp.PixelOffsetMode = PixelOffsetMode.None;
      gBmp.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
      gBmp.TextContrast = 0;
    }


  }
}